#include <stdio.h>
#include <stdlib.h>

#define ATTR_READ_ONLY  0x01
#define ATTR_HIDDEN     0x02
#define ATTR_SYSTEM     0x04
#define ATTR_VOLUME_ID  0x08
#define ATTR_DIRECTORY  0x10
#define ATTR_ARCHIVE    0x20
#define ROOT_DIR        reservedSectors + (fatSize * fats)
#define FILE_TYPE 0x1
#define DIR_TYPE  0x2

typedef struct {
    int sektor;
    int size;
    int type;
    unsigned int addr;
}MyFile;


unsigned short bytesPerSec;
unsigned char sectoresPerCluster;
unsigned short reservedSectors;
unsigned char osName[8];
unsigned char fats;
unsigned short maxRootEntrys;
unsigned short sectorsAviable;
unsigned char mediaType;
unsigned short fatSize;
unsigned short secPerTrack;
unsigned short numHeads;
unsigned int hiddenSectors;
unsigned int toSec32;
unsigned char drvNum;
unsigned char reserved;
unsigned char bootSig;
unsigned int volId;
unsigned char volName[12];
unsigned char fsType[9];
int rootDirSize;
int datensektorenStartsektor;
unsigned short FAT[2880];
unsigned char puffer[512];

unsigned char* readSectorLBA(int lba) {
    /*
    int cylinder = lba / (2 * 18);
    int temp = lba % (2 * 18);
    int head = temp / 18;
    int sektor = temp % 18 + 1;

    printf("lba: %d\n", lba);
    printf("head: %d, cylinder: %d, track: %d\n", head, cylinder, sektor);

    return readSector(head, cylinder, sektor);
    */
    long position = 0;

    FILE *quelle;
    int i;

    quelle = fopen("/home/rsickinger/test/img/testimg", "r+b");

    if (quelle != NULL) {
        position = lba * 512;
        fseek(quelle, position, SEEK_SET);
        fread(&puffer, sizeof(char), 512, quelle);
    }

    return &puffer;
}

void initFilesystem() {
    int i,j;

    unsigned char *sek = readSectorLBA(0);

    for (i = 3, j = 0; i < 8+3; i++, j++)
        osName[j] = sek[i];
    bytesPerSec = sek[11] | (sek[12] << 8);
    sectoresPerCluster = sek[13];
    reservedSectors = sek[14] | (sek[15] << 8);
    fats = sek[16];
    maxRootEntrys = sek[17] | (sek[18] << 8);
    sectorsAviable = sek[19] | (sek[20] << 8);
    mediaType = sek[21];
    fatSize = sek[22] | (sek[23] << 8);
    secPerTrack = sek[24] | (sek[25] << 8);
    numHeads = sek[26] | (sek[27] << 8);
    hiddenSectors = sek[28] | (sek[29] << 8) | (sek[30] << 16) | (sek[31] << 24);
    toSec32 = sek[32] | (sek[33] << 8) | (sek[34] << 16) | (sek[35] << 24);
    drvNum = sek[36];
    reserved = sek[37];
    bootSig = sek[38];
    volId = sek[39] | (sek[40] << 8) | (sek[41] << 16) | (sek[42] << 24);
    for (i = 43, j = 0; i < 43+11; i++, j++)
        volName[j] = sek[i];
    volName[11] = '\0';
    for (i = 54, j = 0; i < 54+8; i++, j++)
        fsType[j] = sek[i];
    fsType[8] = '\0';

    rootDirSize = ((32 * maxRootEntrys) + (bytesPerSec - 1)) / bytesPerSec;
    datensektorenStartsektor = reservedSectors + (fatSize * fats) + rootDirSize;


    readFAT();
}

/*
0.11111111 AB
1.22221111 CD
2.22222222 EF
3.11111111 GH
4.22221111 IJ
5.22222222 KL

ab cd ef  ==>   dab efc
*/

unsigned short up(unsigned char b1,unsigned char b2) {
    unsigned short erg = 0;

    b2 &= 0x0F;
    erg |= b1;
    erg |= (b2 << 8);

    return erg;
}

unsigned short lo(unsigned char b1,unsigned char b2) {
    unsigned short erg = 0;

    b1 &= 0xF0;
    erg |= (b1 >> 4);
    erg |= (b2 << 4);

    return erg;
}

void readFAT() {
    int sektors = 0, aktEntry = 0, i,j = 0, first = 0;;
    unsigned char b[4608];

    while (sektors < 9) {
        unsigned char *sek = readSectorLBA(sektors + 1);

        for (i = 0; i < 512;i++) {
            b[j] = sek[i];
            j++;
        }

        sektors++;
    }

    j = 0;
    while (aktEntry < 2880) {
        FAT[aktEntry] = up(b[j], b[j+1]);
        aktEntry++;
        j++;
        FAT[aktEntry] = lo(b[j], b[j+1]);
        aktEntry++;
        j += 2;
    }
}

MyFile readDir(int sektor, const char *fname) {
    MyFile f;
    int i,j, entry = 0;
    int startSektor = sektor;
    unsigned char *sek = readSectorLBA(startSektor);
    unsigned char aktEntry[32];
    unsigned char name[9];

    //printf("RootDirEntrys:\n\n");

    f.size = f.sektor = -1;

    while (entry < maxRootEntrys) {
        for (i = 0, j = entry; i < 32; i++, j++) {
            aktEntry[i] = sek[j];
        }

        if (aktEntry[0] == 0x0)
            break;

        for (i = 0; i < 8; i++) {
            name[i] = aktEntry[i];
        }

        name[sizeof(name) - 1] = '\0';
        printf("Name: %s", name);

        unsigned short startSec = aktEntry[26] | (aktEntry[27] << 8);
        f.sektor = startSec;
        printf("SSektor: %d  ", startSec);

        int fileSize = aktEntry[28] | (aktEntry[29] << 8) | (aktEntry[30] << 16) | (aktEntry[31] << 24);
        f.size = fileSize;
        printf("Size: %d(Byte) %d(kB)  \n", fileSize, fileSize/1024);

        unsigned char attr = aktEntry[11];
        if (attr & ATTR_DIRECTORY) {
            f.type = DIR_TYPE;
        } else {
            f.type = FILE_TYPE;
        }

        if (strcmp(name, fname) == 0) {
            printf("%s found\n", name);
            return f;
        }
        entry+=32;
    }

    return f;
}

//path: #|texte|text
MyFile decodePathToFile(char *path) {
    MyFile f;
    f.size = f.sektor = -1;

    int size, i, j;
    int startSektor = ROOT_DIR;

    for (size = 0; path[size] != '\0'; size++);

    if (size == 2) {
        printf("Pfad zu kurz\n");
        return f;
    }

    if (path[0] != '#') {
        printf("Pfad verlangt ein Rootdir: #!\n");
        return f;
    }

    if (path[1] != '|') {
        printf("Pfad stimmt nicht ganz\n");
        return f;
    }

    i = 2;
    j = 0;
    int x;
    char fdname[9];
    while (1) {
        if (path[i] == '|' || path[i] == '\0') {
            for (x = j; x < 8; x++) {
                fdname[x] = 0x20;
            }
            fdname[8] = '\0';
            j = 0;
            f = readDir(startSektor, fdname);
            if (f.type == DIR_TYPE) {
                startSektor = f.sektor + datensektorenStartsektor - 2;
                i++;
                continue;
            } else if (f.type == FILE_TYPE) {
                return f;
            }
        } else
            fdname[j] = path[i];
        i++;
        j++;
    }



    return f;
}



/*das passt nicht*/

MyFile readFile(int entry, int size) {
    MyFile f;
    f.size = f.sektor = -1;
    unsigned char file[size];
    unsigned int j = 0, i = 0;
    int newEntry;

    while (1) {
        unsigned char *sek = readSectorLBA(datensektorenStartsektor - 2 + entry);
        entry = FAT[entry];

        for (i = 0; i < 512; i++) {
            file[j] = sek[i];
            j++;
        }

        if (entry >= 0xFF8)
            break;
    }


    // just for test
    for (i = 0; i < size; i++) {
        printf("%x ", file[i]);
        if ((i % 26) == 0)
            printf("\n");
    }

    f.size = size;
    f.sektor = entry;
    f.addr = file;
    return f;
}

void diskInfo() {
    printf("DiskInfo:\n");
    printf("OSName: %s\n", osName);
    printf("Bytes pro Sektor: %d\n", bytesPerSec);
    printf("Sektoren pro Cluster: %d\n", sectoresPerCluster);
    printf("Anzahl der reservierten Sektoren: %d\n", reservedSectors);
    printf("Anzahl der FAT tabellen: %d\n", fats);
    printf("Anzahl moeglicher Eintraege in ROOT: %d\n", maxRootEntrys);
    printf("Anzahl vorhandener Sektoren: %d\n", sectorsAviable);
    printf("Mediatype (0xF0 fuer Diskette): %x\n", mediaType);
    printf("FAT groesse: %d\n", fatSize);
    printf("Sektoren Pro Track: %d\n", secPerTrack);
    printf("Anzahl der Heads: %d\n", numHeads);
    printf("Anzahl der versteckten Sektoren: %d\n", hiddenSectors);
    printf("toSec32: %d\n", toSec32);
    printf("Laufwerknummer: %d\n", drvNum);
    printf("Bootsignatur: %d\n", bootSig);
    if (bootSig == 0x29) {
        printf("Bootsignatur vorhanden\n");
        printf("   VolumeID: %d\n", volId);
        printf("   VolumeName: %s\n", volName);
        printf("   Filesystem Typ: %s\n", fsType);
    } else
        printf("Bootsignatur nicht Vorhanden\n");
}
/*formel fuer sektor addressierung: eintrag - 2*/
int main(void) {
    initFilesystem();
    diskInfo();
    readFAT();
    //readDir(reservedSectors + (fatSize * fats));


    /*
    int i;
    for(i = 0; i < 2880; i++)
    	printf("FAT[%d]=%#x\n", i, FAT[i]);
    */

    //printf("cd GRUB");

    //printf("datensektorenStartsektor: %d\n", datensektorenStartsektor);
    MyFile f = decodePathToFile("#|PROGS|PROG");
    readFile(f.sektor, f.size);
}
